package worker;

import java.util.concurrent.Semaphore;

import main.Block;
import main.Partition;

/**
 * 
 * Worker, that tries to split splitBlock on postBlock.
 * 
 */
public class PostWorker implements Runnable {

	/**
	 * block that we want to split
	 */
	private Block splitBlock;
	/**
	 * partition
	 */
	private Partition partition;
	/**
	 * postBlock on which we try to split the splitBlock.
	 */
	private Block postBlock;

	/**
	 * counts the number of results.
	 */
	private Semaphore resultCounter;
	/**
	 * Counts the number of positive results. The result is positive if the
	 * block is split by some thread.
	 */
	private Semaphore positiveResult;

	/**
	 * Creates a new instance of the PostWorker
	 * 
	 * @param splitBlock
	 *            block, that should be split.
	 * @param partition
	 *            partition
	 * @param postBlock
	 *            postBlock that may split the splitBlock.
	 * @param resultCounter
	 *            counts the number of results.
	 * @param positiveResult
	 *            counts the number of positive results.
	 */
	public PostWorker(Block splitBlock, Partition partition, Block postBlock,
			Semaphore resultCounter, Semaphore positiveResult) {

		this.splitBlock = splitBlock;
		this.partition = partition;
		this.postBlock = postBlock;
		this.resultCounter = resultCounter;
		this.positiveResult = positiveResult;
	}

	/**
	 * Given splitBlock and one postBlock, perform time-consuming computations,
	 * whether we can split the split-block on this postBlock. If yes, we split.
	 * Especially, computation of preset, intersection and difference consumes a
	 * lot of time.
	 */
	public void run() {

		// compute preset of the post-block
		Block preset = postBlock.pre();
		// compute intersection of preset and the block we want to split
		Block intersect = preset.intersect(splitBlock);
		// compute difference of split-block and intersection
		Block difference = splitBlock.difference(intersect);

		// now compute the condition, whether we need to split the split-block.
		boolean condition = !difference.isEmpty() && !intersect.isEmpty();

		// if condition is true, then we may try to split.
		if (condition) {

			// isSplitted is Semaphore with capacity 1.
			// Try to figure out if somebody was faster and already splits
			// this block. If we are the first, acquire the ticket.
			if (!splitBlock.isSplitted.tryAcquire())
				return;

			// if we where lucky and can split the split-block then we do
			// so.
			partition.split(splitBlock, intersect, difference);
			// as the split-block is now not in the partition, we do not
			// need consider it anymore. Free up the resources by removing
			// references to irrelevant post-blocks.
			splitBlock.clearIrrelevantPostBlocks();

			// As the split-block is now split, we increase the number of
			// positive results. Positive means, that the block is split on
			// this post-block. Negative if the block is not split on the
			// post-block.
			positiveResult.release();
		} else {
			// if this post-block cannot split the split-block now, then it
			// cannot split it in the future, add it to the set of irrelevant
			// post-blocks.
			splitBlock.setIrrelevantPostBlock(postBlock);
		}

		// one more result is computed
		resultCounter.release();
	}

}
